package miui.xmpp.impl;

import android.util.Log;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import miui.xmpp.XmppClient;
import miui.xmpp.XmppClientListener;
import miui.xmpp.message.Iq;
import miui.xmpp.message.Message;

/**
 * Created by ouyang on 15-8-24.
 */
public class XmppClientImpl implements XmppClient {

    private static final String TAG = "XmppClientImpl";
    private XmppClientListener listener = null;
    private boolean connected = false;
    private EventLoopGroup group;
    private Bootstrap b;
    private ChannelFuture f;

    public XmppClientImpl(XmppClientListener listener) {
        this.listener = listener;
    }

    @Override
    public synchronized boolean connect(String ip, int port, int millisecond) {
        boolean ret = false;

        do {
            if (connected) {
                break;
            }

            group = new NioEventLoopGroup();

            b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            //p.addLast(new LoggingHandler(LogLevel.INFO));
                            p.addLast(new LineBasedFrameDecoder(1024));
                            p.addLast(new StringDecoder());
                            p.addLast(new ClientHandler());
                        }
                    });

            try {
                f = b.connect(ip, port);
                f.await(millisecond);
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }

            connected = f.isSuccess();
            ret = connected;
        } while (false);

        return ret;
    }

    @Override
    public synchronized boolean disconnect() {
        boolean ret = false;

        do {
            if (! connected) {
                break;
            }

            try {
                f.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                group.shutdownGracefully();
            }

            ret = true;
            connected = false;
        } while (false);

        return ret;
    }

    @Override
    public synchronized boolean isConnected() {
        return connected;
    }

    @Override
    public String getSelfIp() {
        return null;
    }

    @Override
    public boolean send(Iq iq) {
        return send(iq.toString());
    }

    @Override
    public boolean send(Message message) {
        return send(message.toString());
    }

    @Override
    public boolean send(String message) {
        Log.d(TAG, "send: " + message);

        boolean result = false;

        do {
            if (message == null) {
                break;
            }

            ByteBuf buffer = Unpooled.buffer(message.length());
            buffer.writeBytes(message.getBytes());

            f.channel().writeAndFlush(buffer);
            result = true;
        } while (false);

        return result;
    }

    public class ClientHandler extends ChannelHandlerAdapter {
//        private final ByteBuf firstMessage;

        public ClientHandler() {
//            String hello = "hello world";
//            firstMessage = Unpooled.buffer(hello.length());
//            for (int i = 0; i < firstMessage.capacity(); i++) {
//                firstMessage.writeByte(hello.getBytes()[i]);
//            }
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            Log.d(TAG, "channelActive");

//            ctx.writeAndFlush(firstMessage);
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            Log.d(TAG, "channelRead: " + msg);
//            ctx.write(msg);
        }

        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) {
            Log.d(TAG, "channelReadComplete");
            ctx.flush();
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            // Close the connection when an exception is raised.
            cause.printStackTrace();
            ctx.close();
        }
    }
}
